﻿
using SIG.Core;
using SIG.Data.Mapping;
using SIG.Model;
using SIG.Model.Ads;
using SIG.Model.Albums;
using SIG.Model.Announcements;
using SIG.Model.Articles;
using SIG.Model.Blogs;
using SIG.Model.Case;
using SIG.Model.Chronicles;
using SIG.Model.DataAcquisition;
using SIG.Model.Emails;
using SIG.Model.FQAs;
using SIG.Model.Identity;
using SIG.Model.Jobs;
using SIG.Model.Mapping;
using SIG.Model.Shipping;
using SIG.Model.Store;
using SIG.Model.Teams;
using SIG.Model.Videos;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace SIG.Data.UnitOfWork
{
    public class MainBCUnitOfWork : DbContext, IMainBCUnitOfWork 
    {

        public MainBCUnitOfWork() : base("Name=SigContext")
        {
            //Database.SetInitializer(new MainBCUnitOfWorkInitializer());
            //this.Configuration.LazyLoadingEnabled = false;
            //this.Configuration.ProxyCreationEnabled = false;
        }

        #region Fileds

        private IDbSet<Log> _logs;
        private IDbSet<Category> _categories;

        private IDbSet<MenuCategory> _menuCategories;
        private IDbSet<Menu> _menus;
        private IDbSet<Page> _pages;
        private IDbSet<PageMeta> _pageMetas;
        private IDbSet<User> _users;
        private IDbSet<Role> _roles;
        private IDbSet<UserSafety> _userSafeties;
        private IDbSet<Position> _positions;
        private IDbSet<Carousel> _carousels;
        private IDbSet<ArticleCategory> _articleCategories;
        private IDbSet<Article> _articles;
        private IDbSet<Product> _products;
        private IDbSet<ProductPhoto> _productPhotos;

        private IDbSet<GoodsCategory> _goodsCategories;
        private IDbSet<Goods> _goods;
        private IDbSet<GoodsPhoto> _goodsPhotos;
        private IDbSet<Cart> _carts;
        private IDbSet<Order> _orders;
        private IDbSet<OrderDetail> _orderDetails;

        private IDbSet<Province> _provinces;
        private IDbSet<City> _cities;
        private IDbSet<District> _districts;
        private IDbSet<RecipientInfo> _recipientInfoes;

        private IDbSet<LinkCategory> _linkCategories;
        private IDbSet<Link> _links;

        private IDbSet<QuestionCategory> _questionCategories;
        private IDbSet<Question> _questions;

        private IDbSet<Announcement> _announcements;
        private IDbSet<Video> _videos;
        private IDbSet<Email> _emails;
        private IDbSet<EmailAccount> _emailAccounts;
        private IDbSet<EmailTemplate> _emailTemplates;

        private IDbSet<Chronicle> _chronicles;

        private IDbSet<Team> _teams;
        private IDbSet<Job> _jobs;

        private IDbSet<Album> _albums;
        private IDbSet<Photo> _photos;

        private IDbSet<Work> _works;
        private IDbSet<WorkType> _workTypes;
        private IDbSet<Client> _clients;
        private IDbSet<Solution> _solutions;

        private IDbSet<PostCategory> _postCategories;
        private IDbSet<Post> _posts;

        private IDbSet<FilterTemplate> _filterTemplates;
        #endregion

        #region Properties
        public IDbSet<Log> Logs
        {
            get
            {
                if (this._logs == null)
                    this._logs = this.Set<Log>();
                return this._logs;
            }
        }

        public IDbSet<Category> Categories
        {
            get
            {
                if (this._categories == null)
                    this._categories = this.Set<Category>();
                return this._categories;
            }
        }

        public IDbSet<Menu> Menus
        {
            get
            {
                if (this._menus == null)
                    this._menus = this.Set<Menu>();
                return this._menus;
            }
        }
        public IDbSet<Page> Pages
        {
            get
            {
                if (this._pages == null)
                    this._pages = this.Set<Page>();
                return this._pages;
            }
        }

        public IDbSet<PageMeta> PageMetas
        {
            get
            {
                if (this._pageMetas == null)
                    this._pageMetas = this.Set<PageMeta>();
                return this._pageMetas;
            }
        }

        public IDbSet<Carousel> Carousels
        {
            get
            {
                if (this._carousels == null)
                    this._carousels =this.Set<Carousel>();
                return this._carousels;
            }
        }

        public IDbSet<User> Users
        {
            get
            {
                if (this._users == null)
                    this._users = this.Set<User>();
                return this._users;
            }
        }

        public IDbSet<Role> Roles
        {
            get
            {
                if (this._roles == null)
                    this._roles = this.Set<Role>();
                return this._roles;
            }
        }
        public IDbSet<UserSafety> UserSafeties
        {
            get
            {
                if (_userSafeties == null)
                    _userSafeties = Set<UserSafety>();
                return _userSafeties;
            }
        }

        public IDbSet<Article> Articles
        {
            get
            {
                if (this._articles == null)
                    this._articles = this.Set<Article>();
                return this._articles;
            }
        }
        public IDbSet<Product> Products
        {
            get
            {
                if (this._products == null)
                    this._products = this.Set<Product>();
                return this._products;
            }
        }

        public IDbSet<ProductPhoto> ProductPhotos
        {
            get
            {
                if (this._productPhotos == null)
                    this._productPhotos = this.Set<ProductPhoto>();
                return this._productPhotos;
            }
        }

        public IDbSet<GoodsCategory> GoodsCategories
        {
            get
            {
                if (this._goodsCategories == null)
                    this._goodsCategories = this.Set<GoodsCategory>();
                return _goodsCategories;
            }

        }

        public IDbSet<Goods> Goods
        {
            get
            {
                if (_goods == null)
                    _goods = Set<Goods>();
                return _goods;
            }
        }

        public IDbSet<GoodsPhoto> GoodsPhotos
        {
            get
            {
                if (_goodsPhotos == null)
                    _goodsPhotos = Set<GoodsPhoto>();
                return _goodsPhotos;
            }          
        }

        public IDbSet<Cart> Carts
        {
            get
            {
                if (_carts == null)
                    _carts = Set<Cart>();
                return _carts;
            }

           
        }

        public IDbSet<Order> Orders
        {
            get
            {
                if (_orders == null)
                    _orders = Set<Order>();
                return _orders;
            }

        
        }

        public IDbSet<OrderDetail> OrderDetails
        {
            get
            {
                if (_orderDetails == null)
                    _orderDetails = Set<OrderDetail>();
                return _orderDetails;
            }         
        }

        public IDbSet<ArticleCategory> ArticleCategories
        {
            get
            {
                if (_articleCategories == null)
                    _articleCategories = Set<ArticleCategory>();
                return _articleCategories;
            }          
        }

        public IDbSet<Province> Provinces
        {
            get
            {
                if (_provinces == null)
                    _provinces = Set<Province>();
                return _provinces;
            }           
        }

        public IDbSet<City> Cities
        {
            get
            {
                if (_cities == null)
                    _cities = Set<City>();
                return _cities;
            }

        }

        public IDbSet<District> Districts
        {
            get
            {
                if (_districts == null)
                    _districts = Set<District>();
                return _districts;
            }

        }

        public IDbSet<RecipientInfo> RecipientInfoes
        {
            get
            {
                if (_recipientInfoes == null)
                    _recipientInfoes = Set<RecipientInfo>();
                return _recipientInfoes;
            }

          
        }
        public IDbSet<LinkCategory> LinkCategories
        {
            get
            {
                if (_linkCategories == null)
                    _linkCategories = Set<LinkCategory>();
                return _linkCategories;
            }


        }

        public IDbSet<Link> Links
        {
            get
            {
                if (_links == null)
                    _links = Set<Link>();
                return _links;
            }

        }

        public IDbSet<QuestionCategory> QuestionCategories
        {
            get
            {
                if (_questionCategories == null)
                    _questionCategories = Set<QuestionCategory>();
                return _questionCategories;
            }
        }

        public IDbSet<Question> Questions
        {
            get
            {
                if (_questions == null)
                    _questions = Set<Question>();
                return _questions;
            }
        }


        public IDbSet<Announcement> Announcements
        {
            get
            {
                if (_announcements == null)
                    _announcements = Set<Announcement>();
                return _announcements;
            }
        }

        public IDbSet<Video> Videos
        {
            get
            {
                if (_videos == null)
                    _videos = Set<Video>();
                return _videos;
            }

        }

        public IDbSet<Email> Emails
        {
            get
            {
                if (_emails == null)
                    _emails = Set<Email>();
                return _emails;
            }

         
        }

        public IDbSet<EmailAccount> EmailAccounts
        {
            get
            {
                if (_emailAccounts == null)
                    _emailAccounts = Set<EmailAccount>();
                return _emailAccounts;
            }         
        }

        public IDbSet<EmailTemplate> EmailTemplates
        {
            get
            {
                if (_emailTemplates == null)
                    _emailTemplates = Set<EmailTemplate>();
                return _emailTemplates;
            }

        
        }

        public IDbSet<Chronicle> Chronicles
        {
            get
            {
                if (_chronicles == null)
                    _chronicles = Set<Chronicle>();
                return _chronicles;
            }
           
        }

        public IDbSet<Album> Albums
        {
            get
            {
                if (_albums == null)
                    _albums = Set<Album>();
                return _albums;
            }           
        }

        public IDbSet<Photo> Photos
        {
            get
            {
                if (_photos == null)
                    _photos = Set<Photo>();
                return _photos;
            }
        }

        public IDbSet<Team> Teams
        {
            get
            {
                if (_teams == null)
                    _teams = Set<Team>();
                return _teams;
            }

        }

        public IDbSet<Job> Jobs
        {
            get
            {
                if (_jobs == null)
                    _jobs = Set<Job>();
                return _jobs;
            }            
        }

        public IDbSet<Position> Positions
        {
            get
            {
                if (_positions == null)
                    _positions = Set<Position>();
                return _positions;
            }
        }

        public IDbSet<Work> Works
        {
            get
            {
                if (_works == null)
                    _works = Set<Work>();
                return _works;
            }            
        }

        public IDbSet<WorkType> WorkTypes
        {
            get
            {
                if (_workTypes == null)
                    _workTypes = Set<WorkType>();
                return _workTypes;
            }
            
        }

        public IDbSet<Client> Clients
        {
            get
            {
                if (_clients == null)
                    _clients = Set<Client>();
                return _clients;
            }

          
        }

        public IDbSet<Solution> Solutions
        {
            get
            {
                if (_solutions == null)
                    _solutions = Set<Solution>();
                return _solutions;
            }            
        }

        public IDbSet<PostCategory> PostCategories
        {
            get
            {
                if (_postCategories == null)
                    _postCategories = Set<PostCategory>();
                return _postCategories;
            }

        
        }

        public IDbSet<Post> Posts
        {
            get
            {
                if (_posts == null)
                    _posts = Set<Post>();
                return _posts;
            }      
        }


        public IDbSet<FilterTemplate> FilterTemplates
        {
            get
            {
                if (_filterTemplates == null)
                    _filterTemplates = Set<FilterTemplate>();
                return _filterTemplates;
            }

        }

        public IDbSet<MenuCategory> MenuCategories
        {
            get
            {
                if (_menuCategories == null)
                    _menuCategories = Set<MenuCategory>();
                return _menuCategories;
            }          
        }



        #endregion

        #region IQueryableUnitOfWork
        public virtual IQueryable<TEntity> CreateSet<TEntity>() where TEntity : class,new()
        {
            return (IDbSet<TEntity>)this.Set<TEntity>();
        }

        public virtual void Attach<TEntity>(TEntity item) where TEntity : class
        {
            this.Entry<TEntity>(item).State = EntityState.Unchanged;
        }

        public virtual void SetModified<TEntity>(TEntity item) where TEntity : class
        {
            this.Entry<TEntity>(item).State = EntityState.Modified;
        }

        public virtual void ApplyCurrentValues<TEntity>(TEntity original, TEntity current) where TEntity : class
        {
            this.Entry<TEntity>(original).CurrentValues.SetValues((object)current);
        }

        #endregion

        #region IUnitOfWork
        public virtual void Commit()
        {
            try
            {
                this.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                throw this.GetDBValidationExptions(ex);
            }
        }

        public async Task CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            try
            {
                await this.SaveChangesAsync(cancellationToken);
            }
            catch (DbEntityValidationException ex)
            {
                throw this.GetDBValidationExptions(ex);
            }
        }

        public void CommitAndRefreshChanges()
        {
            bool saveFailed = false;

            do
            {
                try
                {
                    base.SaveChanges();

                }
                catch (DbUpdateConcurrencyException ex)
                {
                    saveFailed = true;

                    ex.Entries.ToList()
                              .ForEach(entry =>
                              {
                                  entry.OriginalValues.SetValues(entry.GetDatabaseValues());
                              });

                }
                catch (DbEntityValidationException dbEx)
                {
                    foreach (var entry in this.ChangeTracker.Entries())
                    {
                        if (entry.State == EntityState.Added || entry.State == EntityState.Modified)
                        {
                            entry.State = EntityState.Detached;
                        }
                    }

                    throw GetDBValidationExptions(dbEx);
                    
                }
            } while (saveFailed);
        }

        public async Task CommitAndRefreshChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            bool saveFailed = false;

            do
            {
                try
                {
                    await base.SaveChangesAsync(cancellationToken);

                }
                catch (DbUpdateConcurrencyException ex)
                {
                    saveFailed = true;

                    ex.Entries.ToList()
                              .ForEach(entry =>
                              {
                                  entry.OriginalValues.SetValues(entry.GetDatabaseValues());
                              });

                }
                catch (DbEntityValidationException dbEx)
                {
                    foreach (var entry in this.ChangeTracker.Entries())
                    {
                        if (entry.State == EntityState.Added || entry.State == EntityState.Modified)
                        {
                            entry.State = EntityState.Detached;
                        }
                    }

                    throw GetDBValidationExptions(dbEx);
                }

            } while (saveFailed);
        }

        public void RollbackChanges()
        {
             this.ChangeTracker.Entries().ToList().ForEach((entry => entry.State = EntityState.Unchanged));
        }

        #endregion

        #region ISsql
        public IEnumerable<TEntity> ExecuteQuery<TEntity>(string sqlQuery, params object[] parameters)
        {
            return (IEnumerable<TEntity>)this.Database.SqlQuery<TEntity>(sqlQuery, parameters);
        }

        public int ExecuteCommand(string sqlCommand, params object[] parameters)
        {
            return this.Database.ExecuteSqlCommand(sqlCommand, parameters);
        }

        #endregion

        #region DbContext
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            modelBuilder.Configurations.Add(new MenuCategoryMap());
            modelBuilder.Configurations.Add(new MenuMap());
            modelBuilder.Configurations.Add(new LogMap());
            modelBuilder.Configurations.Add(new PositionMap());
            modelBuilder.Configurations.Add(new CarouselMap());

            modelBuilder.Configurations.Add(new CategoryMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new ProductPhotoMap());

            modelBuilder.Configurations.Add(new ArticleCategoryMap());
            modelBuilder.Configurations.Add(new ArticleMap());
            modelBuilder.Configurations.Add(new PageMap());

            modelBuilder.Configurations.Add(new PageMetaMap());
            modelBuilder.Configurations.Add(new UserMap());
            modelBuilder.Configurations.Add(new RoleMap());
            modelBuilder.Configurations.Add(new UserSafetyMap());

            modelBuilder.Configurations.Add(new GoodsCategoryMap());
            modelBuilder.Configurations.Add(new GoodsMap());
            modelBuilder.Configurations.Add(new GoodsPhotoMap());

            modelBuilder.Configurations.Add(new CartMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new OrderDetailMap());

            modelBuilder.Configurations.Add(new ProvinceMap());
            modelBuilder.Configurations.Add(new CityMap());
            modelBuilder.Configurations.Add(new DistrictMap());
            modelBuilder.Configurations.Add(new RecipientInfoMap());

            modelBuilder.Configurations.Add(new LinkCategoryMap());
            modelBuilder.Configurations.Add(new LinkMap());
            modelBuilder.Configurations.Add(new QuestionCategoryMap());
            modelBuilder.Configurations.Add(new QuestionMap());


            modelBuilder.Configurations.Add(new AnnouncementMap());
            modelBuilder.Configurations.Add(new VideoMap());
            modelBuilder.Configurations.Add(new EmailMap());
            modelBuilder.Configurations.Add(new EmailAccountMap());
            modelBuilder.Configurations.Add(new EmailTemplateMap());

            modelBuilder.Configurations.Add(new ChronicleMap());
            modelBuilder.Configurations.Add(new JobMap());
            modelBuilder.Configurations.Add(new TeamMap());

            modelBuilder.Configurations.Add(new AlbumMap());
            modelBuilder.Configurations.Add(new PhotoMap());

            modelBuilder.Configurations.Add(new ClientMap());
            modelBuilder.Configurations.Add(new SolutionMap());
            modelBuilder.Configurations.Add(new WorkMap());
            modelBuilder.Configurations.Add(new WorkTypeMap());

            modelBuilder.Configurations.Add(new PostCategoryMap());
            modelBuilder.Configurations.Add(new PostMap());

            modelBuilder.Configurations.Add(new FilterTemplateMap());

        }
        public override int SaveChanges()
        {
            var modifiedEntries = ChangeTracker.Entries()
                .Where(x => x.Entity is IAuditableEntity
                    && (x.State == System.Data.Entity.EntityState.Added || x.State == System.Data.Entity.EntityState.Modified));

            foreach (var entry in modifiedEntries)
            {
                IAuditableEntity entity = entry.Entity as IAuditableEntity;
                if (entity != null)
                {
                    string identityName = Thread.CurrentPrincipal.Identity.Name;
                    DateTime now = DateTime.Now;

                    if (entry.State == EntityState.Added)
                    {
                        entity.CreatedBy = identityName;
                        entity.CreatedDate = now;
                    }
                    else {
                        base.Entry(entity).Property(x => x.CreatedBy).IsModified = false;
                        base.Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                    }

                    entity.UpdatedBy = identityName;
                    entity.UpdatedDate = now;
                }
            }

            return base.SaveChanges();
        }

        #endregion

        #region local
        private Exception GetDBValidationExptions(DbEntityValidationException dbEx)
        {
            string message = string.Empty;
            foreach (DbEntityValidationResult validationResult in dbEx.EntityValidationErrors)
            {
                foreach (DbValidationError dbValidationError in (IEnumerable<DbValidationError>)validationResult.ValidationErrors)
                    message = message + string.Format("Property: {0} Error: {1}", (object)dbValidationError.PropertyName, (object)dbValidationError.ErrorMessage);
            }
            return new Exception("有错误", new Exception(message));
        }

        #endregion
    }
}
